import ReactiveElement from "../core/ReactiveElement.js"; // eslint-disable-line no-unused-vars
import * as calendar from "./calendar.js";
import {
  defaultState,
  raiseChangeEvents,
  rendered,
  setState,
  state,
} from "./internal.js";

/**
 * Adds locale-sensitive date support.
 *
 * This mixin is typically used in creating calendar components. All of the Elix
 * calendar components attempt to provide full [international calendar
 * support](CalendarMonth#international-support) to the extent currently
 * possible in the user's web browser.
 *
 * @module CalendarElementMixin
 * @param {Constructor<ReactiveElement>} Base
 */
export default function CalendarElementMixin(Base) {
  // The class prototype added by the mixin.
  class CalendarElement extends Base {
    attributeChangedCallback(name, oldValue, newValue) {
      if (name === "date") {
        this.date = new Date(newValue);
      } else {
        super.attributeChangedCallback(name, oldValue, newValue);
      }
    }

    /**
     * The date that should be shown by the element. For elements that show a
     * range of dates (a month, a week, etc.), the referenced date will be
     * included in the range.
     *
     * This property can be set as a string, which will be parsed and converted
     * to a JavaScript `Date`.
     *
     * If not specified, the default `date` value is the current date.
     *
     * @type {Date}
     */
    get date() {
      return this[state].date;
    }
    set date(date) {
      // Only update state if actual date value differs from current state.
      if (!calendar.datesEqual(date, this[state].date)) {
        this[setState]({ date });
      }
    }

    // @ts-ignore
    get [defaultState]() {
      return Object.assign(super[defaultState] || {}, {
        date: null,
        locale: navigator.language,
      });
    }

    /**
     * A string that identifies a language and a region using a BCP 47
     * language tag. This is the same format used by to identify a
     * locale in the standard `Intl` internationalization API.
     * See the
     * [locales argument](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl#locales_argument)
     * documentation for details.
     *
     * Calendar elements are region-sensitive, so language alone is
     * insufficient. Accordingly, the locale should identify at least a language
     * and a region. Examples: "en-US" identifies US English, while "en-GB"
     * identifies English in Great Britain. The use of "en" on its own would
     * be insufficient.
     *
     * @type {string}
     */
    get locale() {
      return this[state].locale;
    }
    set locale(locale) {
      this[setState]({
        locale: String(locale),
      });
    }

    [rendered](/** @type {ChangedFlags} */ changed) {
      if (super[rendered]) {
        super[rendered](changed);
      }

      // TODO: call calendar.datesEqual(date, previousState.date)?
      if (changed.date && this[raiseChangeEvents]) {
        const date = this[state].date;
        /**
         * Raised when the `date` property changes.
         *
         * @event datechange
         */
        const event = new CustomEvent("datechange", {
          bubbles: true,
          detail: { date },
        });
        this.dispatchEvent(event);
      }
    }
  }

  return CalendarElement;
}
